using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Text.RegularExpressions;
using CMUSHrun.Objects;

namespace CMUSHrun.Server
{
    /// <summary>
    /// The connection class for the TCP connection
    /// </summary>
    class TcpConnection
    {

        /// <summary>
        /// An event raised there is a completed command on the TCP stream
        /// </summary>
        public event EventHandler<TcpStreamTextReceivedEventArgs> TcpStreamTextReceivedEvent;

        /// <summary>
        /// Event when a connection is dropped
        /// </summary>
        public event EventHandler<TcpStreamDisconnectEventArgs> Disconnected;

        /// <summary>
        /// The TCP socket we are connected to
        /// </summary>
        private Socket socket;

        private StreamReader Reader;
        private StreamWriter Writer;

        /// <summary>
        /// The pointer to my instance object
        /// </summary>
        private MushPlayer _avatar = null;

        /// <summary>
        /// Semaphore for locking on user input
        /// </summary>
        static object BigLock = new object();

        /// <summary>
        /// Public constructor. Opens the Stream handler for the socket
        /// </summary>
        /// <param name="socket">The socket handle for this connection</param>
        public TcpConnection(Socket socket)
        {
            this.socket = socket;
            Reader = new StreamReader(new NetworkStream(socket, false));
            Writer = new StreamWriter(new NetworkStream(socket, true));
            new Thread(ClientLoop).Start();
        }

        /// <summary>
        /// Disconnect the user from the engine
        /// </summary>
        public void Disconnect()
        {
            // Tell the player good bye
            WriteLine("Connection closed.");
            socket.Close();
            
            // Release the player connection
            // BindMushPlayer(null);
        }

        /// <summary>
        /// Associates a player object to this socket
        /// </summary>
        /// <param name="user">The game player object to listen to</param>
        public void BindMushPlayer(MushPlayer user)
        {
            // If we are already listening for a player object, we need to disconnect from that player
            if (_avatar != null)
            {
                _avatar.TextToObjectEvent -= new EventHandler<MushPlayerTextPostEventArgs>(_avatar_TextToObjectEvent);
            }

            // Bind to the new player object
            _avatar = user;

            // If we are now connected to a player, bind a new event handler
            if (user != null)
            {
                _avatar.TextToObjectEvent += new EventHandler<MushPlayerTextPostEventArgs>(_avatar_TextToObjectEvent);
            }
        }


        /// <summary>
        /// Returns the connected state of the TCP channel
        /// </summary>
        public Boolean IsLoggedIn
        {
            get
            {
                return (_avatar != null);
            }
        }

        /// <summary>
        /// The loop for the thread
        /// </summary>
        private void ClientLoop()
        {
            try
            {
                lock (BigLock)
                {
                    OnConnect();
                }

                // Perform the process while we are connected
                while (socket.Connected)
                {
                    Writer.Flush();

                    string line = Reader.ReadLine();
                    if (line == null)
                    {
                        break;
                    }
                    lock (BigLock)
                    {
                        ProcessLine(line);
                    }
                }
            }
            finally
            {
                lock (BigLock)
                {
                    // raise the notification we left
                    OnSocketDisconnect();

                    // And release the object
                    BindMushPlayer(null);
                }
            }
        }

        /// <summary>
        /// Called when a new user connection is made
        /// </summary>
        protected void OnConnect()
        {
            Writer.Flush();
            Writer.WriteLine("Welcome!");
            Writer.WriteLine("Connection established to the Server.");
        }

        /// <summary>
        /// Called when the user connection is dropped.
        /// </summary>
        protected void OnSocketDisconnect()
        {
            try
            {
                // If we have lost our connection attempt to close it all out
                Writer = null;
                Reader = null;
                // Release our handle on the avatar
                if (_avatar != null)
                {
                    _avatar.TextToObjectEvent -= new EventHandler<MushPlayerTextPostEventArgs>(_avatar_TextToObjectEvent);
                }

                // Close the socket
                socket.Close();
            }
            finally
            {
                // Announce to the world we are TCP disconnected
                if (Disconnected != null && _avatar != null)
                {
                    Disconnected(this, new TcpStreamDisconnectEventArgs(_avatar));
                }
            }
        }

        /// <summary>
        /// Called when there is new user data on the input channel.
        /// </summary>
        /// <param name="line"></param>
        protected void ProcessLine(string line)
        {
            try
            {
                Writer.WriteLine("You typed: " + line);
            }
            catch
            {
                OnSocketDisconnect();
            }

            if (TcpStreamTextReceivedEvent != null)
            {
                TcpStreamTextReceivedEvent(this, new TcpStreamTextReceivedEventArgs(_avatar, line.Trim()));
            }
            return;
            
        }

        /// <summary>
        /// Handles the connection data when the user is not logged into the system
        /// </summary>
        /// <param name="line">the text supplied on teh source line</param>
        private void HandleLogin(string line)
        {
            
        }

        /// <summary>
        /// A public method that writes the string to the output buffer
        /// </summary>
        /// <param name="line"></param>
        public void Write(string line)
        {
           Writer.Write(line);
           Writer.Flush();
        }

        /// <summary>
        /// Writes the text to the output buffer and flushes it
        /// </summary>
        /// <param name="line"></param>
        public void WriteLine(string line)
        {
            Writer.WriteLine(line);
            Writer.Flush();
        }

        /// <summary>
        /// Listen for the incoming data stream for this user object
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _avatar_TextToObjectEvent(object sender, MushPlayerTextPostEventArgs e)
        {
            WriteLine("Text form source object: " + e.Source.Name + "> " +  e.Text);
        }
    }

    class TcpStreamTextReceivedEventArgs : EventArgs
    {
        /// <summary>
        /// The game user context
        /// </summary>
        public MushPlayer User;
        
        /// <summary>
        /// The string that was recieved on the socket
        /// </summary>
        public String Text;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="user">The Game avatar app user context</param>
        /// <param name="text">The text that came in on the channel</param>
        public TcpStreamTextReceivedEventArgs(MushPlayer user, String text)
        {
            User = user;
            Text = text;
        }
    }

    class TcpStreamDisconnectEventArgs : EventArgs
    {
        /// <summary>
        /// The game user context
        /// </summary>
        public MushPlayer User;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="user">The Game avatar app user context</param>
        /// <param name="text">The text that came in on the channel</param>
        public TcpStreamDisconnectEventArgs(MushPlayer user)
        {
            User = user;
        }
    }


}
